import com.sun.deploy.security.MozillaMyKeyStore;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 23735
 * Date: 2022-10-25
 * Time: 21:33
 */
class MyTask implements Comparable<MyTask>{
    //要执行的任务
    private Runnable runnable;
    //任务执行的时间
    private long time;
    //step不是具体的时间,是时间间隔
    public MyTask(Runnable runnable,long step){
        this.runnable=runnable;
        this.time=System.currentTimeMillis()+step;
    }

    public long getTime(){
        return this.time;
    }
    //执行任务
    public void run(){
        this.runnable.run();
    }

    //放入队列的元素要能够进行比较
    @Override
    public int compareTo(MyTask o) {
        return (int) (this.getTime()-o.getTime());
    }
}
public class MyTimer{
    //定时器内部要能够存放多个任务,用一个队列组织要执行的任务
    private PriorityBlockingQueue<MyTask> queue=new PriorityBlockingQueue<>();
    //锁,用来解决线程忙等待
    private Object locker=new Object();
    //线程放入队列中
    public void schedule(Runnable runnable,long step){
        MyTask myTask=new MyTask(runnable,step);
        queue.put(myTask);
        synchronized (this.locker) {
            //每插入一个任务都要唤醒线程,让队列重新检查一下是否有任务要执行
            // 防止新插入的线程执行时间在之前堆顶任务之前
            //而线程在wait,可能导致错过最新的线程的执行时间,所以要提前唤醒线程
            this.locker.notify();
        }
    }

    public MyTimer(){
        //创建一个线程扫描队列中的任务是否到达执行时间
        Thread t=new Thread(()->{
            while(true){
                try {
                    //取出堆顶元素
                    MyTask myTask=queue.take();
                    //判断是否到达执行时间
                    long curTime=System.currentTimeMillis();
                    if(curTime<myTask.getTime()){
                        //不到运行时间重新放进去//也可以直接用peek(),这样不用来回放,但用take开销也不大
                        queue.put(myTask);
                        //指定等待时间,防止忙等待
                        synchronized(this.locker){
                            //利用wait是因为wait可以中间被打断,因为中间可能有其他
                            //新的任务出现在队列中,且比之前堆顶的任务执行时间靠前
                            this.locker.wait(myTask.getTime()-curTime);
                        }
                    }else{
                        //时间到了就执行
                        myTask.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //不要忘了创建(启动)线程
        t.start();
    }

    public static void main(String[] args) {
        long time1=System.currentTimeMillis();

        MyTimer myTimer=new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello myTimer");
                System.out.println(System.currentTimeMillis()-time1);
            }
        },3000);

        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2");
                System.out.println(System.currentTimeMillis()-time1);
            }
        },2000);

        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("1");
                System.out.println(System.currentTimeMillis()-time1);
            }
        },1000);
        System.out.println("main");
    }
}
